<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body></body>
<script>
  {
    //
    /**
     * 一开始不知道怎么做多对象合并，所以先做两个对象的合并,并且先做的是改变target的写法
     */
    function mergeTwoObject(target, source) {
      for (let key in source) {
        if (
          typeof target[key] === 'object' &&
          typeof source[key] === 'object' &&
          !Array.isArray(target[key]) &&
          !Array.isArray(source[key])
        ) {
          return mergeTwoObject(target[key], source[key])
        } else {
          return (target[key] = source[key])
        }
      }
    }
  }

  {
    /**
     * 由第一种方案扩展出来的尾递归调用
     * Tail Recursive Call 尾递归调用
     */

    // {
    //   /**
    //    * 第一种错误实现，变成了嵌套的
    //    * init:true,on: {init: true, up: 15, on: {init:true,on: {init: true, up: 15, on: {init:true,on: {init: true, up: 15, on: {…}, ug: 20},ug: 20,up: 15}, ug: 20},ug: 20,up: 15}, ug: 20},ug: 20,up: 15
    //    */
    //   function mergeTRCObject(result, source1, source2) {
    //     const list = new Set(Object.keys(source1), Object.keys(source2))
    //     for (let key of list) {
    //       if (
    //         typeof source1[key] === 'object' &&
    //         typeof source2[key] === 'object' &&
    //         !Array.isArray(source1[key]) &&
    //         !Array.isArray(source2[key])
    //       ) {
    //         result[key] = mergeTRCObject(result, source1[key], source2[key]) // 这里忘了写result[key]导致了结果只有value而不是object
    //       } else {
    //         result[key] = source2[key] || source1[key] // 这里同理缺少key
    //       }
    //       // return result //这里因为在for循环return了，导致只执行了一遍
    //     }
    //     return result
    //   }
    // }

    {
      // 正确解法
      function mergeTRCObject(result = {}, source1, source2, index = 1) {
        // const list = new Set(Object.keys(source1).concat(Object.keys(source2)))

        const list = new Set([...Object.keys(source1), Object.keys(source2)])
        // const list = new Set(Object.keys(source1), Object.keys(source2)) //  这里这么写是会导致只set了第一个参数的key，set只支持传入数组
        for (let key of list) {
          if (
            typeof source1[key] === 'object' &&
            typeof source2[key] === 'object' &&
            !Array.isArray(source1[key]) &&
            !Array.isArray(source2[key])
          ) {
            /**
             * 错误点，if跟else都有
             * 忘了写result[key]导致了结果只有value而不是object
             * 没有初始化result，导致结果是undefined，传进去之后undefined[key]报错了
             */
            result[key] = mergeTRCObject(
              result[key],
              source1[key],
              source2[key],
              index + 1
            )
          } else {
            result[key] = source2[key] || source1[key]
          }
          // return result //这里因为在for循环return了，导致只执行了一遍
        }
        return result
      }
    }
    //     const startTime = performance.now()
    //     mergeTRCObject({}, obj1, obj2)
    //     const endTime = performance.now()
    //     runTime = endTime - startTime
  }

  {
    /**
     * 尝试使用vue的虚拟dom设计模式实现数据对象合并
     * key对应的index数字代表基本数据类型
     * index数组则是引用数据类型合并，可以以存入的index是数字为object合并，字符串为Arr合并，如果类型不同直接替换，相同则push
     * typeof === 'object' 而不是Object
     */
    function mergeObjects({ args, deep = true, deduplication = true }) {
      let result = {}
      // 标识符
      let contrastObj = {}
      console.log('args', args.length)
      for (let index = 0; index < args.length; index++) {
        for (let key in args[index]) {
          if (typeof args[index][key] !== 'object') {
            contrastObj[key] = index
            continue
          }

          if (
            contrastObj[key] &&
            typeof contrastObj[key][contrastObj[key].length - 1] ===
            'number' &&
            Array.isArray(args[index][key])
          ) {
            contrastObj[key].push(index)
            continue
          }
          if (
            contrastObj[key] &&
            typeof contrastObj[key][contrastObj[key].length - 1] ===
            'string' &&
            !Array.isArray(args[index][key])
          ) {
            contrastObj[key].push(String(index))
            continue
          }

          contrastObj[key] = [
            Array.isArray(args[index][key]) ? index : String(index),
          ]
          continue
        }
      }
      // 从标识符中得到最终结果
      for (let key in contrastObj) {
        if (
          typeof contrastObj[key] === 'number' ||
          contrastObj[key].length === 1
        ) {
          result[key] = args[contrastObj[key]][key]
        } else if (
          typeof contrastObj[key][contrastObj[key].length - 1] === 'number'
        ) {
          result[key] = []
          for (let i = 0; i < contrastObj[key].length; i++) {
            result[key] = deduplication
              ? Array.from(
                new Set([...result[key], ...args[contrastObj[key][i]][key]])
              ) // 将set转为数组
              : [...result[key], ...args[contrastObj[key][i]][key]]
          }
        } else if (
          typeof contrastObj[key][contrastObj[key].length - 1] === 'string'
        ) {
          // 这里需要解开，因为mergeObjects用的是...args入参
          result[key] = deep
            ? mergeObjects({
              args: contrastObj[key].map((index) => args[index][key]),
              deep,
              deduplication,
            })
            : Object.assign({}, ...args.map((item) => item[key]))
        }
      }
      return result
    }

    //   const result = mergeObjects({
    //   args: [
    //     { a: { a: { b: 15, c: 18, d: [1, 2, 3] } } },
    //     { a: { a: { b: 10 } } },
    //     { a: { a: { b: 20 } } },
    //     { b: [1, 2, 3, 4] },
    //     { b: [3, 4, 5, 6] },
    //     { b: [5, 6, 7, 8] },
    //   ],
    //   deep: false,
    //   deduplication: false,
    // })
  }

  // 优化点：for in 会访问原型链上所有对象属性，只想循环当前对象，使用hasOwnProperty
</script>

</html>